package com.mallcloud.mall.mall.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mallcloud.mall.app.mapper.AppProcedureMapper;
import com.mallcloud.mall.common.core.constant.enums.RLockEnum;
import com.mallcloud.mall.common.core.constant.enums.mall.PointsProductEnum;
import com.mallcloud.mall.common.core.exception.FailException;
import com.mallcloud.mall.common.data.resolver.TenantResolver;
import com.mallcloud.mall.mall.api.entity.MallPointsProductViewModel;
import com.mallcloud.mall.mall.api.entity.dto.ProPointsProductRedeem;
import com.mallcloud.mall.mall.api.entity.dto.ProRedeemItemBack;
import com.mallcloud.mall.mall.api.entity.vo.MallPointsProductViewVO;
import com.mallcloud.mall.mall.mapper.MallPointsProductViewModelMapper;
import com.mallcloud.mall.mall.service.MallPointsProductViewService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 积分商品视图
 */
@Slf4j
@Service
@AllArgsConstructor
public class MallPointsProductViewServiceImpl extends ServiceImpl<MallPointsProductViewModelMapper, MallPointsProductViewModel> implements MallPointsProductViewService {
	private final AppProcedureMapper procedureMapper;
	private final Redisson redisson;

	@Override
	public Page<MallPointsProductViewVO> pagePointsProduct(Page<MallPointsProductViewVO> page, MallPointsProductViewModel dto) {
		return this.baseMapper.pagePointsProduct(page, dto);
	}

	@Override
	public List<MallPointsProductViewModel> options(MallPointsProductViewModel model) {
		return this.list(new LambdaQueryWrapper<MallPointsProductViewModel>()
				.select(MallPointsProductViewModel::getId, MallPointsProductViewModel::getName)
				.eq(MallPointsProductViewModel::getStatus, PointsProductEnum.ProductStatusEnum.Y.name())
				.like(StrUtil.isNotBlank(model.getName()), MallPointsProductViewModel::getName, model.getName())
				.orderByDesc(MallPointsProductViewModel::getReleaseTime)
		);
	}

	@Override
	public MallPointsProductViewVO getInfo(MallPointsProductViewModel param) {
		return this.baseMapper.getInfo(param);
	}

	@SneakyThrows
	@Override
	public ProPointsProductRedeem redeemItem(ProPointsProductRedeem dto) {
		if (StrUtil.isBlank(dto.getStoreNo())) {
			dto.setStoreNo(TenantResolver.getById().getCode());
		}
		procedureMapper.redeemItem(dto);
		if (!dto.isSuccess()) {
			log.error("失败");
			throw new FailException(dto.getMessage());
		}
		return dto;
	}

	@SneakyThrows
	@Override
	public void redeemItemBack(ProRedeemItemBack dto) {
		procedureMapper.redeemItemBack(dto);
		if (!dto.isSuccess()) {
			log.error("失败");
			throw new FailException(dto.getMessage());
		}
	}

	@Override
	public ProPointsProductRedeem redeem(ProPointsProductRedeem dto) {
		String pointsProductId = dto.getId();
		RLock lock = redisson.getLock(RLockEnum.POINT_GOODS_REDEEM.getKey(pointsProductId));
		lock.lock(10, TimeUnit.SECONDS);
		try {
			if (StrUtil.isBlank(dto.getRedeemMoney())) {
				dto.setRedeemMoney("0");
			}

			MallPointsProductViewModel param = new MallPointsProductViewModel();
			param.setId(pointsProductId);
			MallPointsProductViewModel goods = this.getById(param);

			checkGoods(goods, dto);
			log.info("积分商品数量对应金额与积分额检查: OK");

			return this.redeemItem(dto);
		}
		finally {
			lock.unlock();
		}
	}

	/**
	 * 积分商品数量对应金额与积分额检查
	 *
	 * @param goods  商品信息
	 * @param dto 前端传来商品信息
	 * @return
	 */
	@SneakyThrows
	private boolean checkGoods(MallPointsProductViewModel goods, ProPointsProductRedeem dto) {
		log.info("积分商品数量对应金额与积分额检查...");
		//前端来的数据
		int num = Integer.parseInt(dto.getAmount());//兑换数量
		BigDecimal totalMoney = new BigDecimal(dto.getRedeemMoney());//兑换总金额
		BigDecimal totalPoints = new BigDecimal(dto.getPoints());//兑换总积分
		log.info("兑换数量:{},兑换总金额:{},兑换总积分:{}", num, totalMoney, totalPoints);
		//数据库数据
		String integrals = goods.getPoints();
		BigDecimal unitPoints = new BigDecimal(0);//单价积分
		if (integrals != null) {
			unitPoints = new BigDecimal(goods.getPoints());
		}
		Long giftSurplusStock = goods.getGiftSurplusStock();//剩余件
		BigDecimal redeemMoney = goods.getRedeemMoney();
		BigDecimal unitMoney = new BigDecimal(0);
		if (Objects.nonNull(redeemMoney)) {
			unitMoney = redeemMoney;//单价金额
		}
		log.info("剩余件:{},单价金额:{},单价积分:{}", giftSurplusStock, unitMoney, unitPoints);

		//检查购买数量
		if (num > giftSurplusStock) {
			throw new Exception("商品仅剩" + giftSurplusStock + "件");
		}
		if (totalMoney.compareTo(unitMoney.multiply(new BigDecimal(num))) != 0) {
			throw new Exception("购买失败：总金额错误");
		}
		if (totalPoints.compareTo(unitPoints.multiply(new BigDecimal(num))) != 0) {
			throw new Exception("购买失败：总积分错误");
		}
		return true;
	}
}
